socket编程流程总结

您所在的位置:网站首页 socket 函数 socket编程流程总结

socket编程流程总结

#socket编程流程总结| 来源: 网络整理| 查看: 265

Windows socket网络编程 流程示例 TCP与UDP的区别man----socket编程区别一.TCP流式套接字的编程步骤服务器端程序流程服务器端代码 客户端程序流程客户端代码 二.UDP型套接字的编程步骤服务器(接收)端程序服务器端代码 客户端(发送)端程序客户端端代码 三.聊天程序中的注意事项网络字节序创建socket服务器的步骤Socket函数分析sockaddr地址结构体分析bind函数的分析listen函数分析accept函数分析connect函数分析 小Tips(以后专门整理)

TCP与UDP的区别

基于连接与无连接   对系统资源的要求(TCP较多,UDP少)   UDP程序结构较简单   流模式与数据报模式   TCP保证数据正确性,UDP可能丢包   TCP保证数据顺序,UDP不保证   部分满足以下几点要求时,应该采用UDP 面向数据报方式 网络数据大多为短消息   拥有大量Client   对数据安全性无特殊要求   网络负担非常重,但对响应速度要求高   具体编程时的区别 socket()的参数不同   UDP Server不需要调用listen和accept   UDP收发数据用sendto/recvfrom函数   TCP:地址信息在connect/accept时确定   UDP:在sendto/recvfrom函数中每次均 需指定地址信息   UDP:shutdown函数无效

man----socket

通过查看socket的man手册可以看到socket函数的第一个参数的值可以为下面这些值:   Name Purpose   PF_UNIX, PF_LOCAL Local communication   PF_INET IPv4 Internet protocols   PF_INET6 IPv6 Internet protocols   PF_IPX IPX - Novell protocols   PF_NETLINK Kernel user interface device   PF_X25 ITU-T X.25 / ISO-8208 protocol   PF_AX25 Amateur radio AX.25 protocol   PF_ATMPVC Access to raw ATM PVCs   PF_APPLETALK Appletalk   PF_PACKET Low level packet interface

编程区别

通常我们在说到网络编程时默认是指TCP编程,即用前面提到的socket函数创建一个socket用于TCP通讯,函数参数我们通常填为SOCK_STREAM。即socket(PF_INET, SOCK_STREAM, 0),这表示建立一个socket用于流式网络通讯。   SOCK_STREAM这种的特点是面向连接的,即每次收发数据之前必须通过connect建立连接,也是双向的,即任何一方都可以收发数据,协议本身提供了一些保障机制保证它是可靠的、有序的,即每个包按照发送的顺序到达接收方。

而SOCK_DGRAM这种是User Datagram Protocol协议的网络通讯,它是无连接的,不可靠的,因为通讯双方发送数据后不知道对方是否已经收到数据,是否正常收到数据。任何一方建立一个socket以后就可以用sendto发送数据,也可以用recvfrom接收数据。根本不关心对方是否存在,是否发送了数据。它的特点是通讯速度比较快。大家都知道TCP是要经过三次握手的,而UDP没有。

基于上述不同,UDP和TCP编程步骤也有些不同,如下:   TCP编程的服务器端一般步骤是:   1、创建一个socket,用函数socket();   2、设置socket属性,用函数setsockopt(); * 可选   3、绑定IP地址、端口等信息到socket上,用函数bind();   4、开启监听,用函数listen();   5、接收客户端上来的连接,用函数accept();   6、收发数据,用函数send()和recv(),或者read()和write();   7、关闭网络连接;   8、关闭监听;

TCP编程的客户端一般步骤是:   1、创建一个socket,用函数socket();   2、设置socket属性,用函数setsockopt();* 可选   3、绑定IP地址、端口等信息到socket上,用函数bind();* 可选   4、设置要连接的对方的IP地址和端口等属性;   5、连接服务器,用函数connect();   6、收发数据,用函数send()和recv(),或者read()和write();   7、关闭网络连接;

在这里插入图片描述

与之对应的UDP编程步骤要简单许多,分别如下:   UDP编程的服务器端一般步骤是:   1、创建一个socket,用函数socket();   2、设置socket属性,用函数setsockopt();* 可选   3、绑定IP地址、端口等信息到socket上,用函数bind();   4、循环接收数据,用函数recvfrom();   5、关闭网络连接;

UDP编程的客户端一般步骤是:   1、创建一个socket,用函数socket();   2、设置socket属性,用函数setsockopt();* 可选   3、绑定IP地址、端口等信息到socket上,用函数bind();* 可选   4、设置对方的IP地址和端口等属性;   5、发送数据,用函数sendto();   6、关闭网络连接

一.TCP流式套接字的编程步骤

在使用之前须链接库函数:工程->设置->Link->输入ws2_32.lib

服务器端程序 流程

1、加载套接字库 2、创建套接字(socket)。 3、将套接字绑定到一个本地地址和端口上(bind)。 4、将套接字设为监听模式,准备接收客户请求(listen)。 5、等待客户请求到来;当请求到来后,接受连接请求,返回一个新的对应于此次连接的套接字(accept)。 6、用返回的套接字和客户端进行通信(send/recv)。 7、返回,等待另一客户请求。 8、关闭套接字。

服务器端代码 #include //加裁头文件 #include //加载标准输入输出头文件 void main() { WORD wVersionRequested;//版本号 WSADATA wsaData; int err; wVersionRequested = MAKEWORD( 1, 1 );//1.1版本的套接字 err = WSAStartup( wVersionRequested, &wsaData ); if ( err != 0 ) { return; }//加载套接字库,加裁失败则返回 if ( LOBYTE( wsaData.wVersion ) != 1 ||HIBYTE( wsaData.wVersion ) != 1 ){ WSACleanup( ); return; }//如果不是1.1的则退出 SOCKET sockSrv=socket(AF_INET,SOCK_STREAM,0);//创建套接字(socket)。 SOCKADDR_IN addrSrv; addrSrv.sin_addr.S_un.S_addr=htonl(INADDR_ANY); //转换Unsigned short为网络字节序的格式 addrSrv.sin_family=AF_INET; addrSrv.sin_port=htons(6000); bind(sockSrv,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR)); //将套接字绑定到一个本地地址和端口上(bind) listen(sockSrv,5); //将套接字设为监听模式,准备接收客户请求(listen)。 SOCKADDR_IN addrClient;//定义地址族 int len=sizeof(SOCKADDR);//初始化这个参数,这个参数必须被初始化 while(1){ SOCKET sockConn=accept(sockSrv,(SOCKADDR*)&addrClient,&len); //accept的第三个参数一定要有初始值。 //等待客户请求到来;当请求到来后,接受连接请求, //返回一个新的对应于此次连接的套接字(accept)。 //此时程序在此发生阻塞 char sendBuf[100]; sprintf(sendBuf,"Welcome %s to www.msn.com.cn",inet_ntoa(addrClient.sin_addr)); //用返回的套接字和客户端进行通信(send/recv)。 send(sockConn,sendBuf,strlen(sendBuf)+1,0); char recvBuf[100]; recv(sockConn,recvBuf,100,0); printf("%s\n",recvBuf); closesocket(sockConn);//关闭套接字。等待另一个用户请求 } } 客户端程序 流程

1、加载套接字库 2、创建套接字(socket)。 3、向服务器发出连接请求(connect)。 4、和服务器端进行通信(send/recv)。 5、关闭套接字。

客户端代码 #include #include void main(){ WORD wVersionRequested; WSADATA wsaData; int err; wVersionRequested = MAKEWORD( 1, 1 ); err = WSAStartup( wVersionRequested, &wsaData );//加载套接字库 if ( err != 0 ) { return; } if ( LOBYTE( wsaData.wVersion ) != 1 || HIBYTE( wsaData.wVersion ) != 1 ){ WSACleanup( ); return; } SOCKET sockClient=socket(AF_INET,SOCK_STREAM,0);//创建套接字(socket)。 SOCKADDR_IN addrSrv; addrSrv.sin_addr.S_un.S_addr=inet_addr("127.0.0.1"); addrSrv.sin_family=AF_INET; addrSrv.sin_port=htons(6000); connect(sockClient,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR)); //向服务器发出连接请求(connect)。 char recvBuf[100];//和服务器端进行通信(send/recv)。 recv(sockClient,recvBuf,100,0); printf("%s\n",recvBuf); send(sockClient,"This is blues_j",strlen("This is blues_j")+1,0); closesocket(sockClient);//关闭套接字。 WSACleanup();//必须调用这个函数清除参数 } 二.UDP型套接字的编程步骤 服务器(接收)端程序

1、创建套接字(socket)。 2、将套接字绑定到一个本地地址和端口上(bind)。 3、等待接收数据(recvfrom)。 4、关闭套接字。

服务器端代码 #include #include void main(){ WORD wVersionRequested; WSADATA wsaData; int err; wVersionRequested = MAKEWORD( 1, 1 ); err = WSAStartup( wVersionRequested, &wsaData ); if ( err != 0 ) { return; } if ( LOBYTE( wsaData.wVersion ) != 1 ||HIBYTE( wsaData.wVersion ) != 1 ){ WSACleanup( ); return; } SOCKET sockSrv=socket(AF_INET,SOCK_DGRAM,0); SOCKADDR_IN addrSrv; addrSrv.sin_addr.S_un.S_addr=htonl(INADDR_ANY); addrSrv.sin_family=AF_INET; addrSrv.sin_port=htons(6000); bind(sockSrv,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR)); SOCKADDR_IN addrClient; int len=sizeof(SOCKADDR); char recvBuf[100]; recvfrom(sockSrv,recvBuf,100,0,(SOCKADDR*)&addrClient,&len); printf("%s\n",recvBuf); closesocket(sockSrv); WSACleanup(); } 客户端(发送)端程序

客户端(发送端)程序: 1、创建套接字(socket)。 2、向服务器发送数据(sendto)。 3、关闭套接字。

客户端端代码 #include #include void main(){ WORD wVersionRequested; WSADATA wsaData; int err; wVersionRequested = MAKEWORD( 1, 1 ); err = WSAStartup( wVersionRequested, &wsaData ); if ( err != 0 ) { return; } if ( LOBYTE( wsaData.wVersion ) != 1 ||HIBYTE( wsaData.wVersion ) != 1 ){ WSACleanup( ); return; } SOCKET sockClient=socket(AF_INET,SOCK_DGRAM,0); SOCKADDR_IN addrSrv; addrSrv.sin_addr.S_un.S_addr=inet_addr("127.0.0.1"); addrSrv.sin_family=AF_INET; addrSrv.sin_port=htons(6000); sendto(sockClient,"Hello",strlen("Hello")+1,0, (SOCKADDR*)&addrSrv,sizeof(SOCKADDR)); closesocket(sockClient); WSACleanup(); } 三.聊天程序中的注意事项

聊天程序常用的是UDP式套接字。因为TCP的三步握手开销比较大。 socket通讯

网络字节序

小端法: 高位存高地址,低位存低地址。 (intel架构的存储方式) 大端法:高位存低地址,低位存高地址。(网络传输的方式)

#include

// 将本地转网络,转IP 转192.168.1.11->string->atoi->int->htonl->网络字节序,可以使用 int inet_pton(); 进行直接转换

uint32_t htonl(uint32_t hostlong);

// 本地转网络,转port

uint16_t htons(uint16_t hostshort);

// 网络转本地,转ip

uint32_t ntohl(uint32_t netlong);

// 网络转本地,转port

uint16_t ntohs(uint16_t netshort);

// string转网络字节

int inet_pton(int af, const char * restrict src, void * restrict dst);

// af: AF_INET, AF_INET6 // src: ip地址,点分十进制 // dst: 转换之后的 网络字节序的地址

创建socket服务器的步骤

创建socket句柄 bind() 绑定ip+port listen() 设置监听上线,同时连接数 accept() 阻塞监听客户端连接 read() 进行数据的读取,读取到的数据需要toupper()进行小写转大写 write() 写入,写给客户端返回值 当read()读到0就是close的时候 进行 close() 关闭

Socket函数分析

创建一个套接字

int socket(int domain, int type, int protocol);

domain : 所选用的ip地址协议, AF_INET, AF_INET6 type : 类型 SOCK_STREAM(TCP/流形式), SOCK_DGRAM(UDP/报形式) protocol : 代表协议号 0 返回值:

成功返回0,新套接字的文件描述 失败返回: -1 #include fd = socket(AF_INET, SOCK_STREAM, 0) sockaddr地址结构体分析 #include #include struct sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_port = htons(9527); int dst; inet_pton(AF_INET, "192.168.22.45", (void *)&dst); addr.sin_addr.s_addr = dst; addr.sin_addr.s_addr = htonl(INADDR_ANY)

INADDR_ANY 取出系统有效的任意ip地址,是二进制类型

bind(fd, (struct sockaddr *)&addr, size); bind函数的分析

绑定函数

bind(fd, (struct sockaddr *)&addr, size);

fd : socket文件对象 (struct sockaddr *)&addr : 将sockaddr存储的地址结构进行强制转换成sockaddr 进行传入 size : addr的大小,使用sizeof进行获取

listen函数分析

监听函数

int listen(int sockfd, int backlog);

sockfd : 套接字 backlog : 最大连接数,最大为128 返回值: 0, -1 error

accept函数分析

堵塞函数

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

sockfd : socket 函数返回值 addr : 传出参数, 成功返回服务器的ip和端口号 addrlen : 传入传出。入: addr的大小。 出:客户端addr的实际大小 返回值:

成功:大于0,返回成功的套接字文件描述符 失败:返回-1

connect函数分析 int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

使用现有的socket与服务器建立连接

sockfd: socket 函数返回值 addr: 传入服务器的地址结构 返回值: 成功:0 失败:-1

小Tips(以后专门整理) WM_APP: 想要定义其自己消息的应用程序应该使用WM_APP。WM_APP 是确保不会与系统(WM_CREATE 等等)或类/特定控件消息如DM_GETDEFID 相冲突的。WSACleanup: 每次调用WSAStartup,都要调用相应的WSACleanup;因为每次启动调都会增加加载winsock DLL的引用计数,因此必须调同样多的WSACleanup,以减少引用计数Winsock API: 在“传输层”和“会话层”之间。TCP/UDP: TCP:可靠连接。字节流,无报文边界。 UDP:无确认,不可靠,无连接。有报文边界。端口: 0~1023:系统保留; 1024~65535:用户使用;同时打开的套接字数目:不固定的,与可用的物理内存有关。 7.一般受到SOCKET_ERROR后,除了WSAEWOULDBLOCK, 都应该关闭套接字,因为它不能再用了。recv可以“消息取数”,即偷看。但不好,性能下降(2次系统调用)。recv:把数据留在参数缓冲中不好,那样剩余缓冲量将减少,可能使系统减少发送端TCP窗口容量,从而使网络流量减少。所以最好把接收到的数据复制到自己的buffer中。send: TCP流情况下,发出的字节也许少于理想的字节。解决方法:用一个循环发送。TCP流情况下,10个send, 也许被1-2个recv就全接收了。UDP: 有connect, 可使用send; 无connect, 只可使用sendtoWSAEWOULDBLOCK: 很多情况下,都可能出现。GetLastError: 独立与线程的,线程之间各自有各自的,不影响!!!MailSlot: 客户机到服务器,不可靠,单向。垃圾!消息长了,尽量考虑面向连接,因为无连接分片,丢一个就白折腾了


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3